package com.n.facedetectordemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.media.FaceDetector;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.Toast;

import com.n.facedetectordemo.databinding.ActivityFaceBinding;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import pub.devrel.easypermissions.EasyPermissions;

public class FaceActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {

    ActivityFaceBinding binding;

    private CircleCanvasView circleCanvasView;

    private Camera mCamera;

    private boolean isIdentify = false;

    private int previewWidth;

    private int previewHeight;

    // 由于FaceDetector检测的不稳定性和准确率，这里控制识别成功一定次数后才算真正的通过，也可以优化为连续识别成功，这样准确率更高
    private int passNum = 0;

    private String useFileName = "";

    OkHttpClient client;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityFaceBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        addMasking(binding.frameLayout);

        if (EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE)) {
            startPreview();
        } else {
            EasyPermissions.requestPermissions(this, "请允许相机权限进行人脸检测",
                    100, Manifest.permission.CAMERA,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE);
        }
        initSpinner();

        binding.start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                isIdentify = true;
                binding.start.setText("识别中");
                binding.state.setText("请移入人脸");
            }
        });
    }

    void initSpinner() {
        String[] spinnerItems = {};
        String supFileStr = FileOpUtil.getSDCardRoot4FaceSet();
        File supFile = new File(supFileStr);
        if (supFile.isDirectory()) {
            File[] subFiles = supFile.listFiles();
            List<String> subFilesList = new ArrayList<>();
            for (File file : subFiles) {
                subFilesList.add(file.getName());
            }
            spinnerItems = subFilesList.toArray(new String[subFilesList.size()]);
        }

        if (spinnerItems.length > 0) {
            useFileName = spinnerItems[0];
        }

        //简单的string数组适配器：样式res，数组
        ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(FaceActivity.this,
                android.R.layout.simple_spinner_item, spinnerItems);
        //下拉的样式res
        spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        //绑定 Adapter到控件
        binding.name.setAdapter(spinnerAdapter);

        //选择监听
        String[] finalSpinnerItems = spinnerItems;
        binding.name.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            //parent就是父控件spinner
            //view就是spinner内填充的textview,id=@android:id/text1
            //position是值所在数组的位置
            //id是值所在行的位置，一般来说与positin一致
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                useFileName = finalSpinnerItems[pos];
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                // Another interface callback
            }
        });
    }

    /**
     * 增加圆形蒙层
     *
     * @param view
     */
    private void addMasking(FrameLayout view) {
        circleCanvasView = new CircleCanvasView(this);
        view.addView(circleCanvasView);
    }

    /**
     * 开始预览
     */
    private void startPreview() {
        SurfaceHolder holder = binding.surfaceView.getHolder();

        holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder surfaceHolder) {
                mCamera = getCustomCamera();
                mCamera.setPreviewCallback((data, camera) -> {
                    // 是否已经检测成功，如果是则不继续检测
                    if (isIdentify) {
                        checkFaces(data, camera, surfaceHolder, this);
                    }
                });
            }

            @Override
            public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
                if (mCamera != null) {
                    Camera.Parameters parameters = mCamera.getParameters();
                    // 选择合适的图片尺寸，必须是手机支持的尺寸
                    List<Camera.Size> sizeList = parameters.getSupportedPictureSizes();
                    // 如果sizeList只有一个我们也没有必要做什么了，因为就他一个别无选择
                    if (sizeList.size() > 1) {
                        for (int j = 0; j < sizeList.size(); j++) {
                            Camera.Size size = sizeList.get(j);
                            previewWidth = size.width;
                            previewHeight = size.height;
                        }
                    }
                    //设置照片的大小
                    parameters.setPictureSize(previewWidth, previewHeight);
                    mCamera.setParameters(parameters);
                    try {
                        mCamera.setPreviewDisplay(holder);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    //调用相机预览功能
                    mCamera.startPreview();
                }
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
                if (null != mCamera) {
                    holder.removeCallback(this);
                    mCamera.setPreviewCallback(null);
                    //停止预览
                    mCamera.stopPreview();
                    mCamera.lock();
                    //释放相机资源
                    mCamera.release();
                    mCamera = null;
                }
            }
        });
        binding.state.setText("识别未开始");
    }

    /**
     * 获取打开相机
     *
     * @return
     */
    private Camera getCustomCamera() {
        if (null == mCamera) {
            //Camera.open()方法说明：2.3以后支持多摄像头，所以开启前可以通过getNumberOfCameras先获取摄像头数目，
            // 再通过 getCameraInfo得到需要开启的摄像头id，然后传入Open函数开启摄像头，
            // 假如摄像头开启成功则返回一个Camera对象
            try {

                mCamera = Camera.open(Camera.getNumberOfCameras() - 1);
                //预览画面默认是横屏的，需要旋转90度
                mCamera.setDisplayOrientation(90);
            } catch (Exception e) {
            }
        }
        return mCamera;
    }

    /**
     * 检测是否存在人脸
     *
     * @param data          YUI的图片字节
     * @param camera        相机
     * @param surfaceHolder
     * @param callback
     */
    private void checkFaces(byte[] data, Camera camera, SurfaceHolder surfaceHolder, SurfaceHolder.Callback callback) {
        Camera.Size size = camera.getParameters().getPreviewSize();
        Bitmap bitmap = new FastYUVtoRGB(FaceActivity.this).convertYUVtoRGB(data, size.width, size.height);
        Bitmap bp = rotateMyBitmap(bitmap);
        BitmapFactory.Options BitmapFactoryOptionsbfo = new BitmapFactory.Options();
        BitmapFactoryOptionsbfo.inPreferredConfig = Bitmap.Config.RGB_565;

        Bitmap bitmap565 = bp.copy(Bitmap.Config.RGB_565, true);
        Bitmap newBP = mergeBitmap(bitmap565, getViewBitmap());
//                    FaceDetector face_detector = new FaceDetector(bp.getWidth(), bp.getHeight(), 1);
        FaceDetector face_detector = new FaceDetector(newBP.getWidth(), newBP.getHeight(), 1);
        FaceDetector.Face[] faces = new FaceDetector.Face[1];
        int face_count = face_detector.findFaces(newBP, faces);
        Log.i(">>>face num", String.valueOf(face_count));

        if (face_count > 0) {
            String fileName = "t" + (new Date()).getTime() + ".jpg";
            String t = FileOpUtil.getSDCardRoot4FaceTemp() + "/" + fileName;
            ImageUtils.saveBitmapToFile(bp, t);

            if (passNum == 1) {
                binding.state.setText("准备人脸识别");
            } else if (passNum == 2) {
                mCamera.stopPreview();
                binding.state.setText("人脸识别..");
                binding.loading.setVisibility(View.VISIBLE);
                circleCanvasView.setStatus(CircleCanvasView.Face_Status.working);

                File img1 = new File(FileOpUtil.getSDCardRoot4FaceSet() + "/" + useFileName);
                if (!img1.exists()) {
                    Log.d("Face", " img1 not exist");
                }
                Log.d("Face", img1.getAbsolutePath());

                File img2 = new File(t);
                if (!img2.exists()) {
                    Log.d("Face", " img2 not exist");
                }
                Log.d("Face", img2.getAbsolutePath());

                requestDeepFace(img1, img2);
            }
            passNum++;

            Log.i(">>>人脸数", passNum + "");
        }
    }

    public void requestDeepFace(File file1, File file2) {

        if (client == null) {
            client = new OkHttpClient.Builder()
                    .connectTimeout(30, TimeUnit.SECONDS) // 设置连接超时时间为30秒
                    .callTimeout(120, TimeUnit.SECONDS) // 设置调用超时时间为120秒
                    .pingInterval(5, TimeUnit.SECONDS) // 设置ping间隔时间为5秒
                    .readTimeout(90, TimeUnit.SECONDS) // 设置读取超时时间为60秒
                    .writeTimeout(90, TimeUnit.SECONDS) // 设置写入超时时间为60秒
                    .addInterceptor(new LoggingInterceptor().getLogging())
                    .build();
        }

        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("img1", file1.getName(), RequestBody.create(MediaType.parse("image/*"), file1))
                .addFormDataPart("img2", file2.getName(), RequestBody.create(MediaType.parse("image/*"), file2))
                .addFormDataPart("model_name", "Facenet512")
                .build();

        Request req = new Request.Builder()
                .url("http://192.168.11.163:5005/verify")
                .post(multipartBody)
                .build();

        Call call = client.newCall(req);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                Log.d("onResponse", response.toString());
                if (response.code() == 200) {
                    if (response.body() != null) {
                        String ret = response.body().string();
                        System.out.println("requestDeepFace body == " + ret);
                        try {
                            JSONObject jsonObject = new JSONObject(ret);
                            boolean verified = jsonObject.getBoolean("verified");
                            FaceActivity.this.showRes(verified);
                        } catch (Exception e) {
                            e.printStackTrace();
                            FaceActivity.this.showRes(false);
                        }
                        return;
                    }
                }
                FaceActivity.this.showRes(false);
            }
        });

    }

    public void showRes(boolean verified) {
        FaceActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {

                String mess = "识别失败";
                if (verified) {
                    mess = "识别成功";
                }
                if (mCamera != null) {
                    passNum = 0;
                    mCamera.startPreview();
                }
                binding.loading.setVisibility(View.GONE);
                circleCanvasView.setStatus(verified ? CircleCanvasView.Face_Status.ok : CircleCanvasView.Face_Status.fail);

                new AlertDialog.Builder(FaceActivity.this)
                        .setTitle(mess)
                        .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                binding.state.setText("请移入人脸");
                                circleCanvasView.setStatus(CircleCanvasView.Face_Status.idle);
                                circleCanvasView.invalidate();
                            }
                        })
                        .show();
            }
        });
    }

    /**
     * 旋转相机预览图
     *
     * @param bmp
     * @return
     */
    public Bitmap rotateMyBitmap(Bitmap bmp) {
        //*****旋转一下
        Matrix matrix = new Matrix();
        matrix.postRotate(270);

        Bitmap nbmp2 = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);

        return nbmp2;
    }

    /**
     * 合并两张bitmap图片
     *
     * @param firstBitmap
     * @param secondBitmap
     * @return
     */
    private Bitmap mergeBitmap(Bitmap firstBitmap, Bitmap secondBitmap) {
        Bitmap bitmap = Bitmap.createBitmap(firstBitmap.getWidth(), firstBitmap.getHeight(), Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawBitmap(firstBitmap, new Matrix(), null);
        canvas.drawBitmap(secondBitmap, new Matrix(), null);
        return bitmap;
    }

    /**
     * 传入的activity是要截屏的activity
     */
    public Bitmap getViewBitmap() {
        View rootView = circleCanvasView;
        rootView.buildDrawingCache();

        //允许当前窗口保存缓存信息
        rootView.setDrawingCacheEnabled(true);
        //去掉状态栏高度
        Bitmap bitmap = Bitmap.createBitmap(rootView.getDrawingCache(), 0, 0, rootView.getWidth(), rootView.getHeight());
        return bitmap;
    }

    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        startPreview();
    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        Toast.makeText(this, "权限获取失败，无法进行检测，请开启相应权限~", Toast.LENGTH_SHORT).show();
    }
}